package com.devilwwj.http.core;

import com.devilwwj.http.callback.OnProgressUpdatedListener;
import com.devilwwj.http.core.ICallback;
import com.devilwwj.http.exception.HttpException;
import com.devilwwj.http.utils.TextUtils;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.util.EntityUtils;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.util.zip.GZIPInputStream;
import java.util.zip.InflaterInputStream;

/**
 * Created by devilwwj on 15/7/16.
 */
public abstract class AbstractDataCallback<T> implements ICallback<T> {
    protected String path;
    private boolean isCancelled;

    /**
     * 检查是否被取消
     *
     * @throws HttpException
     */
    public void checkIfIsCancelled() throws HttpException {
        if (isCancelled) {
            throw new HttpException(HttpException.ErrorType.CANCEL, "the request has been canceled");
        }
    }

    @Override
    public T handle(HttpURLConnection connection) throws HttpException {
        return handle(connection, null);
    }

    @Override
    public T handle(HttpURLConnection connection, OnProgressUpdatedListener listener) throws HttpException {
        try {
            checkIfIsCancelled();
            InputStream is = null;
            int status = connection.getResponseCode();
            // TODO normally 200 represent success, bug you should know 200-299 all means success, please ensure with your backend colleagus
            if (status == HttpStatus.SC_OK) {
                // TODO support gzip // deflate
                String encoding = connection.getContentEncoding();
                if (encoding != null && "gzip".equalsIgnoreCase(encoding)) {
                    is = new GZIPInputStream(connection.getInputStream());
                } else if (encoding != null && "defalte".equalsIgnoreCase(encoding)) {
                    is = new InflaterInputStream(connection.getInputStream());
                } else {
                    is = connection.getInputStream();
                }

                if (path == null) {
                    ByteArrayOutputStream out = new ByteArrayOutputStream();
                    byte[] buffer = new byte[2048];
                    int len;
                    while((len = is.read(buffer)) != -1) {
                        checkIfIsCancelled();
                        out.write(buffer, 0, len);
                    }

                    is.close();
                    out.flush();
                    out.close();
                    String result = new String(out.toByteArray());
                    T t = bindData(result);
                    return postRequest(t);
                } else {
                    FileOutputStream out = new FileOutputStream(path);

                    int totalLen = connection.getContentLength();
                    int curLen = 0;
                    byte[] buffer = new byte[2048];
                    int len;
                    int percent = 0;
                    while((len = is.read(buffer)) != -1) {
                        checkIfIsCancelled();
                        out.write(buffer, 0, len);
                        if (curLen * 100l / totalLen > percent) {
                            curLen += len;
                            if (listener != null) {
                                listener.onProgressUpdate(curLen, totalLen);
                            }
                            percent = (int) (curLen * 100l / totalLen);
                        }
                    }
                    is.close();
                    out.flush();
                    out.close();
                    T t = bindData(path);
                    return  postRequest(t);
                }
            } else {
                throw new HttpException(status, connection.getResponseMessage());
            }
        } catch (IOException e) {
            throw new HttpException(HttpException.ErrorType.SERVER, e.getMessage());
        }
    }

    @Override
    public T handle(HttpResponse response) throws HttpException {
        return handle(response, null);
    }

    @Override
    public T handle(HttpResponse response, OnProgressUpdatedListener listener) throws HttpException {
        checkIfIsCancelled();
        try {
            // Object json xml string image
            // 获得返回的实体
            HttpEntity entity = response.getEntity();
            // 获得状态码
            int statusCode = response.getStatusLine().getStatusCode();
            // TODO normally 200 represent success, but you should know 200-299 all means success, please ensure with your backend colleagues
            switch (statusCode) {
                case HttpStatus.SC_OK: // 返回200，表示请求成功
                case HttpStatus.SC_CREATED:
                    if (TextUtils.isValidate(path)) {
                        InputStream is = null;
                        if (entity.getContentEncoding() != null) {
                            String encoding = entity.getContentEncoding().getValue();
                            if (encoding != null && "gzip".equalsIgnoreCase(encoding)) {
                                is = new GZIPInputStream(entity.getContent());
                            } else if (encoding != null && "deflate".equalsIgnoreCase(encoding)) {
                                is = new InflaterInputStream(entity.getContent());
                            }
                        } else {
                            // 获取响应实体的输入流
                            is = entity.getContent();
                        }


                        // 根据文件路径创建文件输出流
                        FileOutputStream fos = new FileOutputStream(path);
//                        InputStream is = entity.getContent();
                        // 两个字节大小的字节数组
                        byte[] buffer = new byte[2048];
                        int len = -1;
                        int percent = 0;
                        // FIXME: if the content length exceed maximum int, please divide 1024 to make it right
                        // 获取内容长度
                        int contentLength = (int)entity.getContentLength();
                        int curPos;
                        curPos = 0;
                        while ((len = is.read(buffer)) != -1) {
                            checkIfIsCancelled();
                            fos.write(buffer, 0, len); // 写入
                            if (curPos * 100l / contentLength > percent) {
                                // 计算下载进度
                                curPos += len;
                                if (listener != null) {

                                    listener.onProgressUpdate(curPos, contentLength);
                                }
                                percent = (int) (curPos * 100l / contentLength);
                            }

                        }
                        // 关闭各种流
                        is.close();
                        fos.flush();
                        fos.close();
                        T t = bindData(path);
                        return postRequest(t);
                    } else {
                        // FIXME: may OOM if the data is very large
                        T t = bindData(EntityUtils.toString(entity, "UTF-8"));
                        return postRequest(t);
                    }
                default:
                    String detailMessage = response.getStatusLine().getReasonPhrase();
                    throw new HttpException(statusCode, detailMessage);
            }
        } catch (IOException e) {
            throw new HttpException(HttpException.ErrorType.SERVER, e.getMessage());
        }
    }

    /**
     * 这个方法交给子类去实现
     *
     * @param content
     * @return
     * @throws HttpException
     */
    public abstract T bindData(String content) throws HttpException;


    /**
     * 父类提供空实现，子类想实现的时候就进行复写
     * 不想实现的时候，就不复写
     *
     * @param curLen
     * @param totalLen
     */
    @Override
    public void onProgressUpdate(int state, int curLen, int totalLen) {

    }


    @Override
    public void cancel() {
        this.isCancelled = true;
    }

    /**
     * 设置缓存文件路径
     *
     * @param path
     * @return
     */
    public ICallback setCachePath(String path) {
        this.path = path;
        return this;
    }

    @Override
    public T postRequest(T t) {
        return t;
    }

    @Override
    public T preRequest() {
        return null;
    }
}
